package com.enzoism.springboot.dive.bis.lock;

import java.util.LinkedList;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 生产者，消费者模式在面试coding中出场率很高，可以用synchronized+wait+ notify来实现，
 * 也可以使用Lock+Condition实现
 *
 * @link https://www.jianshu.com/p/b60273eb71a9
 */
public class LockConditionTest {
    private LinkedList<String> queue = new LinkedList<String>();

    private Lock lock = new ReentrantLock();

    private int maxSize = 5;

    private Condition providerCondition = lock.newCondition();

    private Condition consumerCondition = lock.newCondition();

    public static void main(String[] args) {
        LockConditionTest t = new LockConditionTest();
        ExecutorService executorService = Executors.newFixedThreadPool(40);
        for (int i = 0; i < 40; i++) {
            // 模拟生产能力 > 消费能力
            executorService.submit(new Provider(t));
            executorService.submit(new Provider(t));
            executorService.submit(new Consumer(t));
        }
        executorService.shutdown();
    }

    public void provide(String value) {
        try {
            lock.lock();
            while (queue.size() == maxSize) {
                System.out.println("--------->【生产者】队列已满，生产者必须等待消费者消费");
                providerCondition.await();
            }
            queue.add(value);
            consumerCondition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("--------->【生产者】已经完成数据生产");
            lock.unlock();
        }
    }

    public String consume() {
        String result = null;
        try {
            lock.lock();
            while (queue.size() == 0) {
                consumerCondition.await();
            }
            Thread.sleep(2000);
            result = queue.poll();
            providerCondition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("------------->【消费者】已经完成数据消费");
            lock.unlock();
        }
        return result;
    }

    public static class Provider implements Runnable {
        private LockConditionTest test;

        public Provider(LockConditionTest test) {
            this.test = test;
        }

        @Override
        public void run() {
            test.provide("" + new Random().nextInt(10000));
        }
    }


    public static class Consumer implements Runnable {
        private LockConditionTest test;

        public Consumer(LockConditionTest test) {
            this.test = test;
        }

        @Override
        public void run() {
            test.consume();
        }
    }
}